﻿Imports System.Data.Caching
Public NotInheritable Class CachingService
    Private Shared FactoryIndexList(0) As String
    Private Shared FactoryList(0) As CacheFactory

    Private Shared CacheIndexList(0) As String
    Private Shared CacheList(0) As Cache

    Public Enum CacheListEnum
        Defualt = 0
        Server1 = 1
        Server2 = 2
    End Enum
    ''' <summary>
    ''' Initializes the cahce and adds it to the global array if it hasn't yet been created.
    ''' </summary>
    ''' <param name="CacheName"></param>
    ''' <remarks></remarks>
    Private Shared Sub InitializeCache(Optional ByVal CacheName As CacheListEnum = CacheListEnum.Defualt)
        If Not FactoryIndexList.Contains(CacheName.ToString) Then
            ReDim Preserve FactoryIndexList(FactoryIndexList.Count)
            ReDim Preserve FactoryList(FactoryList.Count)
            ReDim Preserve CacheIndexList(CacheIndexList.Count)
            ReDim Preserve CacheList(CacheList.Count)

            FactoryIndexList(FactoryIndexList.Count - 1) = CacheName.ToString
            CacheIndexList(CacheIndexList.Count - 1) = CacheName.ToString

            Dim MyCacheFactory As CacheFactory
            Dim MyCache As Cache

            'Define Array for 1 Cache Host
            Dim Servers1(0) As ServerEndPoint
            Dim Servers2(0) As ServerEndPoint
            Dim Localservers(0) As ServerEndPoint

            'Specify Cache Host Details 
            '  Parameter 1 = host name
            '  Parameter 2 = cache port number
            '  Parameter 3 = cache service name
            Servers1(0) = New ServerEndPoint("Server1", 22233, "DistributedCacheService")
            Servers2(0) = New ServerEndPoint("Server2", 22233, "DistributedCacheService")
            Localservers(0) = New ServerEndPoint("Localhost", 22233, "DistributedCacheService")

            ' Place code here to handle which servers the cachecluster should hit
            Select Case CacheName
                Case CacheListEnum.Server1
                    MyCacheFactory = New CacheFactory(Servers1, True, False)
                Case CacheListEnum.Server2
                    MyCacheFactory = New CacheFactory(Servers2, True, False)
                    'MyCacheFactory = New CacheFactory(Localservers, True, False)
                Case Else
                    MyCacheFactory = New CacheFactory(Localservers, True, False)
            End Select
            CacheFactory.DisableLogSinks()
            FactoryList(FactoryList.Count - 1) = MyCacheFactory
            Select Case CacheName
                Case CacheListEnum.Defualt
                    MyCache = MyCacheFactory.GetCache(Nothing)
                Case Else
                    MyCache = MyCacheFactory.GetCache(CacheName.ToString)
            End Select
            CacheList(CacheList.Count - 1) = MyCache
        End If
    End Sub
    Public Shared ReadOnly Property MyCache(Optional ByVal CacheName As CacheListEnum = CacheListEnum.Defualt) As Cache
        Get
            InitializeCache(CacheName)
            Dim I As Int32 = Array.IndexOf(CacheIndexList, CacheName.ToString)
            Return CacheList(I)
            'Return CacheList(Array.IndexOf(CacheIndexList, CacheName))
        End Get
    End Property
    Private Sub New()

    End Sub
    ''' <summary>
    ''' Places and object in the cache.
    ''' </summary>
    ''' <param name="Key"></param>
    ''' <param name="Item"></param>
    ''' <param name="Region"></param>
    ''' <param name="TimeInCache"></param>
    ''' <remarks>This can be used inside and outside of the caching scope.
    ''' If a caching scope is active and locking enabled the object will be locked until the scope has been disposed.
    ''' </remarks>
    ''' 
    Public Shared Sub PutCacheItem(ByVal Key As String, ByVal Item As Object, Optional ByVal Region As String = Nothing, Optional ByVal TimeInCache As Int32 = 600)
        PutCacheItem(CacheListEnum.Defualt, Key, Item, Region, TimeInCache)
    End Sub
    Public Shared Sub PutCacheItem(ByVal CacheName As CacheListEnum, ByVal Key As String, ByVal Item As Object, Optional ByVal Region As String = Nothing, Optional ByVal TimeInCache As Int32 = 600)
        InitializeCache()

        Dim O2 As New Object
        O2 = Item

        Dim TS As System.TimeSpan = TimeSpan.FromSeconds(TimeInCache)
        If Region Is Nothing Then
            Region = "DefaultRegion"
        End If
        CheckandCreateRegion(Region, CacheName)
        If O2 Is Nothing Then
            Try
                MyCache(CacheName).Remove(Region, Key)
            Catch ex As Exception

            End Try
        Else
            Try
                If TimeInCache = 0 Then
                    MyCache(CacheName).Put(Region, Key, O2)
                Else
                    MyCache(CacheName).Put(Region, Key, O2, TS)
                End If
                If CachingScope.ScopeActive Then
                    Dim TempObj As Object = MyCache(CacheName).GetAndLock(Region, Key, New TimeSpan(0, 0, 30), CachingScope._LockHandle)
                    CachingScope.AddItemToCacheArray(CacheName, Region, Key, CachingScope._LockHandle, TempObj)
                End If
            Catch ex As Exception
                'Possible failed because the region no longer exists.
                'Retry create region and add object to cache
                CheckandCreateRegion(Region, CacheName)
                If TimeInCache = 0 Then
                    MyCache(CacheName).Put(Region, Key, O2)
                Else
                    MyCache(CacheName).Put(Region, Key, O2, TS)
                End If
                If CachingScope.ScopeActive Then
                    Dim TempObj As Object = MyCache(CacheName).GetAndLock(Region, Key, New TimeSpan(0, 0, 30), CachingScope._LockHandle)
                    CachingScope.AddItemToCacheArray(CacheName, Region, Key, CachingScope._LockHandle, TempObj)
                End If
            End Try
        End If
        O2 = Nothing
    End Sub
    ''' <summary>
    ''' Returns a cached item without locking it.
    ''' </summary>
    ''' <param name="Key"></param>
    ''' <param name="Region"></param>
    ''' <returns></returns>
    ''' <remarks>This can be used inside and outside of the caching scope</remarks>
    Public Shared Function GetCacheItem(ByVal Key As String, Optional ByVal Region As String = Nothing) As Object
        Return GetCacheItem(CacheListEnum.Defualt, Key, Region)
    End Function
    Public Shared Function GetCacheItem(ByVal CacheName As CacheListEnum, ByVal Key As String, Optional ByVal Region As String = Nothing) As Object
        InitializeCache()

        If Region Is Nothing Then
            Region = "DefaultRegion"
        End If
        Dim TempObj As Object = Nothing
        Try
            TempObj = MyCache(CacheName).Get(Region, Key)
        Catch ex As Exception
            'Object was not found
        End Try

        Return TempObj
    End Function
    ''' <summary>
    ''' Return a cached item if it exists and locks it.
    ''' </summary>
    ''' <param name="Key"></param>
    ''' <param name="Region"></param>
    ''' <returns></returns>
    ''' <remarks>This must be run inside a caching scope.</remarks>
    ''' 
    Public Shared Function GetandLcokCacheItem(ByVal Key As String, Optional ByVal Region As String = Nothing) As Object
        Return GetandLcokCacheItem(CacheListEnum.Defualt, Key, Region)
    End Function
    Public Shared Function GetandLcokCacheItem(ByVal CacheName As CacheListEnum, ByVal Key As String, Optional ByVal Region As String = Nothing) As Object
        If Not CachingScope.ScopeActive Then
            Throw New Exception("You can only lock cached items within the context of a and active CachingScope.")
        End If

        InitializeCache()

        If Region Is Nothing Then
            Region = "DefaultRegion"
        End If

        Dim TempObj As Object
        TempObj = CachingScope.CheckandGetCachedItem(Region, Key)
        If TempObj Is Nothing Then
            Do While True
                Try
                    TempObj = MyCache(CacheName).GetAndLock(Region, Key, New TimeSpan(0, 0, 30), CachingScope._LockHandle)
                    CachingScope.AddItemToCacheArray(CacheName, Region, Key, CachingScope._LockHandle, TempObj)
                    Exit Do
                Catch ex As CacheException
                    Select Case Left(ex.Message, 20)
                        Case "ErrorCode<ERRCA0008>" 'Key doesn't exist
                            Exit Do
                        Case "ErrorCode<ERRCA0013>" 'Key already locked
                            'Spin the thread
                            Threading.Thread.Sleep(1000)
                    End Select
                Catch ex As Exception
                    Throw ex
                End Try
            Loop
        End If

        Return TempObj
    End Function
    ''' <summary>
    ''' Will Create a region if one doesn't exist but will not overwrite an existing region.
    ''' </summary>
    ''' <param name="Region">Region Name</param>
    ''' <param name="CacheName">Cache Object to search</param>
    ''' <remarks></remarks>
    Public Shared Sub CheckandCreateRegion(ByVal Region As String, Optional ByVal CacheName As CacheListEnum = CacheListEnum.Defualt)
        InitializeCache(CacheName)

        Dim RegionCacheString As String
        RegionCacheString = "Default_" & Region
        If ActiveRegionList Is Nothing Then
            ActiveRegionList = New ArrayList
        End If

        If Not ActiveRegionList.Contains(CacheName & "_" & RegionCacheString) Then
            Try
                MyCache(CacheName).CreateRegion(Region, False)
            Catch ex As Exception
            End Try
            ActiveRegionList.Add(CacheName & "_" & RegionCacheString)
        End If
    End Sub
    ''' <summary>
    ''' This method should not be used since it is not thread safe.
    ''' </summary>
    ''' <param name="Region"></param>
    ''' <param name="MyCache"></param>
    ''' <remarks></remarks>
    <Obsolete("Not Thread Safe", True)> _
    Protected Shared Sub RemoveRegion(ByVal Region As String, ByRef MyCache As Cache)
        Dim RegionCacheString As String

        RegionCacheString = "Default_" & Region

        If ActiveRegionList Is Nothing Then
            ActiveRegionList = New ArrayList
        End If

        Try
            MyCache.RemoveRegion(Region)
            ActiveRegionList.Remove(RegionCacheString)
        Catch ex As Exception
        End Try
    End Sub
    Protected Shared Sub RemoveRegionFromRegionList(ByVal Region As String, ByRef MyCache As Cache)
        Dim RegionCacheString As String
        If MyCache Is Nothing Then
            RegionCacheString = "Default_" & Region
        Else
            RegionCacheString = MyCache.ToString & "_" & Region
        End If
        If ActiveRegionList Is Nothing Then
            ActiveRegionList = New ArrayList
        End If
        ActiveRegionList.Remove(RegionCacheString)
    End Sub
    ''' <summary>
    ''' Holds the list of active regions so that a lokup against the caching service doe snot need to be performed.
    ''' </summary>
    ''' <remarks></remarks>
    Private Shared ActiveRegionList As ArrayList
End Class